package com.ht.api.db;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import com.ht.api.db.bean.Column;
import com.ht.api.db.bean.DbSql;
import com.ht.api.db.event.DbEventManager;
import com.ht.api.db.exception.HtSqlException;
import com.ht.api.db.hlink.HlinkManager;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import cn.hutool.json.JSONNull;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 数据库操作类
 * @author asq
 * @createTime 2024年11月29日 14:04:10
 */
@Slf4j
public class Db{
	/** 批量插入时，每批次的数据量 */
	private static final int BATCH_SIZE = 1000;
	
	/**
	 * 本类为纯静态调用
	 */
    private Db() {
	}
    
	/**
     * 创建Entity
     * @param table 表名
     * @return
     */
    public static Entity createEntity(String table) {
    	return Entity.create(table);
    }
    
    /**
     * 创建SqlParser
     * @param table 表名
     * @return
     */
    public static SqlParser createSqlParser(String table) {
    	return SqlParser.create(table);
    }
    
    /**
     * 默认数据源：删除
     * @param bean 数据
     * @return
     */
	public static int delete(Entity bean) {
		bean.set("sys_deleted", 0);
		return insert(DbConfig.useSysDb(), bean);
	}
    
    /**
     * 指定数据源：删除
     * @param bean 数据
	 * @param ds 指定数据源
     * @return
     */
	public static int delete(DataSource ds, Entity bean) {
		bean.set("sys_deleted", 0);
		return insert(ds, bean);
	}
	
	/**
	 * 默认数据源：插入
	 * @param bean 数据
	 * @return
	 */
	public static int insert(Entity bean) {
		return insert(DbConfig.useSysDb(), bean);
	}
	
	/**
	 * 插入
	 * @param ds 指定数据源
	 * @param bean 数据
	 * @return
	 */
	public static int insert(DataSource ds, Entity bean) {
		// 设置固定字段
		bean.set("sys_time", DateUtil.now());
		if (!bean.containsKey("sys_deleted")) {
			bean.set("sys_deleted", 1);
		}
		
		// 排除一些有函数但又不需要修改的字段
		filterFunctionField(bean, DbConfig.columnList(bean.getTable()));
		
		// 执行
		DbSql dbSql = getInsertSql(bean);
		int result = call(ds, dbSql.getSql(), null, false, dbSql.getParamsList().toArray());
		
		// 触发insert事件
		DbEventManager.onInsert(ds, bean);
		
		// 触发Hlink
		HlinkManager.start(ds, bean);
		
		return result;
	}

	/**
	 * 默认数据源：异步方式批量插入数据
	 * @param datalist 数据列表
	 */
	public static void insertBatchAsync(List<Entity> datalist) {
		insertBatch(DbConfig.useSysDb(), datalist, true);
	}
	
	/**
	 * 默认数据源：同步方式批量插入数据
	 * @param datalist 数据列表
	 */
	public static void insertBatchSync(List<Entity> datalist) {
		insertBatch(DbConfig.useSysDb(), datalist, false);
	}
	
	/**
	 * 批量插入数据
	 * @param ds 指定数据源
	 * @param datalist 数据列表
	 * @param async 是否异步
	 */
	public static void insertBatch(DataSource ds, List<Entity> datalist, boolean async) {
		if (datalist == null || datalist.size() == 0) {
			log.warn("[db]执行批量insert，列表为空，忽略执行~");
			return;
		}
		
		// 设置固定字段
		datalist.stream().forEach(bean -> {
			bean.set("sys_time", DateUtil.now());
			if (!bean.containsKey("sys_deleted")) {
				bean.set("sys_deleted", 1);
			}
		});
		
		// 记录执行耗时
		TimeInterval timer = DateUtil.timer();
		
		// 将所有数据库资源对象放在下面这个对象里，方便关闭
		DbResource dbr = DbResource.create(ds);
		try {
			Connection connection = dbr.getConnection();
			connection.setAutoCommit(false); // 开启事务
			
			// 异步，针对selectDB
			if (async) {
				Statement asyncStatement = dbr.createStatement();
				asyncStatement.execute("SET group_commit = async_mode;");
			}
			
			// 将每个表装成一组
			Map<String, List<Entity>> dataMap = getMapGroupByTableName(datalist);
			for (String table : dataMap.keySet()) {
				List<Entity> dblist = dataMap.get(table);
				List<Column> columnList = DbConfig.columnList(table);
				
				// 排除一些有函数但又不需要修改的字段
				for (Entity entity : datalist) {
					filterFunctionField(entity, columnList);
				}
				
				// 生成insert into语句开头部分
				String insertInto = getFieldSql(table, columnList);
				
				// insert语句的values部分
				StringBuilder sql = new StringBuilder();
				boolean sqlCreated = false; // sql是否已创建（批量时每页sql都一样，勿须重复创建）
				for (int m = 0; m < dblist.size(); m += BATCH_SIZE) {
					List<Entity> batchlist = dblist.subList(m, Math.min(m + BATCH_SIZE, dblist.size()));
					boolean isBatch = batchlist.size() == BATCH_SIZE; // 是否是正常的满页
					if (!isBatch) {
						sqlCreated = false; // 如果最后一页不是满页时，则将sqlCreated置为false，以便重新生成sql
						sql.setLength(0); // 清空
					}
					
					// 每一批插入所有的参数
					List<Object> paramsList = new ArrayList<>();
					
					// 追加多个values语句，同时获得参数值
					for (Entity b : batchlist) {
						// 获取sql参数对象
						DbSql valueSql = getValueSqlBean(b, columnList);
						
						// 加入总的参数列表
						paramsList.addAll(valueSql.getParamsList());
						
						// 如果sql还未创建，则创建之
						if (!sqlCreated) {
							sql.append(valueSql.getSql()).append(",");
						}
					}
					if (!sqlCreated) {
						sql.deleteCharAt(sql.length() - 1);
						sql.insert(0, insertInto);
						sqlCreated = true; // 标识sql已创建，下一页批量时不再重复创建了
					}
					
					// 添加批量操作
					PreparedStatement batchStatement = dbr.prepareBatchStatement(sql.toString());
					setParam(batchStatement, paramsList.toArray());
					batchStatement.addBatch();
				}
			}
			
			// 执行批量操作
			for (PreparedStatement statement : dbr.getPsList()) {
				statement.executeBatch();
			}
			
			// 提交事务
			connection.commit();
			
			// 触发Hlink
			HlinkManager.start(ds, dataMap);
		} catch (SQLException e) {
			dbr.rollback();
			throw new RuntimeException("[db]批量insert异常：" + e.getMessage(), e);
		} finally {
			dbr.close();
		}
		
		log.debug("[db]执行批量insert，耗时：{}ms", timer.intervalPretty());
	}
	
	/**
	 * 批量插入数据（使用StreamLoad）（使用系统数据源）
	 * @param batchDataList 数据列表
	 */
	public static void insertBatchWithStream(List<Entity> batchDataList) {
		insertBatchWithStream(DbConfig.useSysSLC(), batchDataList);
	}

	/**
	 * 批量插入数据（使用StreamLoad）（指定数据源）
	 * @param config selectDB的StreamLoad配置对象，从{@link DbConfig}对象中获取
	 * @param batchDataList 数据列表
	 */
	public static void insertBatchWithStream(StreamLoadConfig config, List<Entity> batchDataList) {
		if (batchDataList == null || batchDataList.size() == 0) {
			log.warn("[db]执行批量insert，列表为空，忽略执行~");
			return;
		}
		
		// 将每个表的数据装成一组，进行提交
		Map<String, List<Entity>> dataMap = getMapGroupByTableName(batchDataList);
		for (String table : dataMap.keySet()) {
			List<Entity> dataList = dataMap.get(table);
			
			// 排除一些有函数但又不需要修改的字段
			List<Column> columnList = DbConfig.columnList(table);
			for (Entity entity : dataList) {
				filterFunctionField(entity, columnList);
			}
			
			// 注入一些自动字段
			dataList.stream().forEach(b -> {
				if (!b.containsKey("main_id")) {
					b.set("main_id", b.getMainId());
				}
				b.set("sys_time", DateUtil.now()).remove("table");
				if (!b.containsKey("sys_deleted")) {
					b.set("sys_deleted", 1);
				}
			});
			
			// 获取StreamLoad的http地址
			String urlPattern = "http://%s:%d/api/%s/%s/_stream_load";
			String url = String.format(urlPattern, config.getFeHost(), config.getFePort(), config.getDbName(), table);
			
			// 分批处理
			int totalSize = dataList.size();
			for (int i = 0; i < totalSize; i += config.getBatchSize()) {
				int endIndex = Math.min(i + config.getBatchSize(), totalSize);
				List<Entity> batch = dataList.subList(i, endIndex);
				if (!batch.isEmpty()) {
					sendBatchToSelectDB(config, batch, url, DbConfig.getColumns(table));
				}
			}
		}
		
		// 触发Hlink
		HlinkManager.start(config, dataMap);
	}
	
	/**
	 * 调用selectDB的StreamLoad，批量写入数据
	 * @param config  StreamLoad的配置
	 * @param records 数据列表
	 * @param streamLoadUrl StreamLoad的地址
	 * @param columns 插入数据的列名串，格式如：field1,field2,...
	 */
	private static void sendBatchToSelectDB(StreamLoadConfig config, List<Entity> records, 
			String streamLoadUrl, String columns) {
	    try {
	    	// 1. 预先计算批量大小
	        int batchSize = records.size();
	        long estimatedDataSize = records.stream().mapToLong(r -> r.toString().length()).sum();
	        
	        // 2. 根据数据量动态调整参数
	        HttpRequest request = HttpUtil.createRequest(Method.PUT, streamLoadUrl);
	        request.setConnectionTimeout(30000)
	               .setReadTimeout(Math.max(30000, batchSize * 10)) // 根据批量大小调整超时
	               .setFollowRedirects(true);
	        
	        // 3. 设置优化的请求头
	        String auth = config.getUsername() + ":" + config.getPassword();
	        String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
	        request.header("Authorization", "Basic " + encodedAuth)
				.header("format", "json")
				.header("strip_outer_array", "true")
				.header("columns", columns)
				.header("Content-Type", "application/json")
				.header("Expect", "100-continue")
				.header("max_filter_ratio", "0.1") // 1. 调整过滤比例
				.header("timeout", String.valueOf(config.getTimeoutSeconds())) // 2. 调整超时时间
				.header("merge_type", "APPEND") // 3. 选择合适的导入模式
				
				// 4. 添加性能相关参数
	            .header("max_batch_rows", String.valueOf(Math.min(batchSize, 500000))) // 单批次最大行数
				.header("max_batch_size", String.valueOf(Math.min(estimatedDataSize, 104857600))) // 单批次最大大小（100MB）
				.header("strict_mode", "false")  // 非严格模式可提高性能
				// 5. 并发控制
				.header("send_batch_parallelism", "3")
				.header("load_parallelism", "3");

	        // 4. 优化JSON构建
			int initialCapacity = (int) Math.min(estimatedDataSize + 2, Integer.MAX_VALUE);
			StringBuilder jsonBuilder = new StringBuilder(initialCapacity);
			jsonBuilder.append('[');
			for (int i = 0; i < records.size(); i++) {
			    jsonBuilder.append(records.get(i).toString()).append(",");
			}
			jsonBuilder.deleteCharAt(jsonBuilder.length() - 1).append(']');
	        request.body(jsonBuilder.toString());
	        
	        log.info("streamLoad:{}", jsonBuilder.toString());

	        // 5. 执行请求并监控
	        long startTime = System.currentTimeMillis();
	        HttpResponse response = request.execute();
	        long endTime = System.currentTimeMillis();
	        
	        // 6. 记录性能指标
	        log.info("[db]Stream Load完成: 状态={}, 耗时={}ms, 批量大小={}, 数据量={}bytes", 
	                response.getStatus(), (endTime - startTime), batchSize, estimatedDataSize);
	                
	        if (!response.isOk()) {
	            log.error("[db]Stream Load失败: {}", response.body());
	            throw new RuntimeException("Stream Load failed: " + response.body());
	        } else {
	        	JSONObject result = JSONUtil.parseObj(response.body());
	        	if (!"Success".equals(result.getStr("Status"))) {
	        		log.error("[db]Stream Load失败：{}", result.getStr("Message"));
	        	}
	        	
	        	log.info("[db]Stream Load返回: {} - {}", response.getStatus(), response.body());
	        }
	    } catch (Exception e) {
	        log.error("[db]Stream Load处理失败", e);
	        throw new RuntimeException("[db]Stream Load处理失败：" + streamLoadUrl, e);
	    }
	}

	/**
	 * 将所有entity对象按表名分组，放入一个map，方便批量插入时使用
	 * @param datalist 所有entity对象的列表 
	 * @return
	 */
	public static Map<String, List<Entity>> getMapGroupByTableName(List<Entity> datalist) {
		Map<String, List<Entity>> dataMap = new HashMap<>();
		for (Entity entity : datalist) {
			String table = entity.getTable();
			if (!dataMap.containsKey(table)) {
				dataMap.put(table, new ArrayList<Entity>());
			}

			dataMap.get(table).add(entity);
		}
		return dataMap;
	}

	/**
	 * 默认数据源：执行sql
	 * @param sql sql代码
	 * @param params 参数
	 * @return
	 */
	public static <T> int execute(String sql, List<T> params) {
		return call(DbConfig.useSysDb(), sql, null, false, params.toArray());
	}
	
	/**
	 * 执行sql
	 * @param ds 指定数据源
	 * @param sql sql代码
	 * @param params 参数
	 * @return
	 */
	public static <T> int execute(DataSource ds, String sql, List<T> params) {
		return call(ds, sql, null, false, params.toArray());
	}

	/**
	 * 默认数据源：执行sql
	 * @param sql sql代码
	 * @param params 参数
	 * @return
	 */
	public static int execute(String sql, Object... params) {
		return call(DbConfig.useSysDb(), sql, null, false, params);
	}
	
	/**
	 * 执行sql
	 * @param ds 指定数据源
	 * @param sql sql代码
	 * @param params 参数
	 * @return
	 */
	public static int execute(DataSource ds, String sql, Object... params) {
		return call(ds, sql, null, false, params);
	}

	/**
	 * 默认数据源：执行查询
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static <T> DbList query(String sql, List<T> params) {
		return call(DbConfig.useSysDb(), sql, r -> read(r, null), false, params.toArray());
	}
	
	/**
	 * 执行查询
	 * @param ds 指定数据源
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static <T> DbList query(DataSource ds, String sql, List<T> params) {
		return call(ds, sql, r -> read(r, null), false, params.toArray());
	}

	/**
	 * 默认数据源：执行查询
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static DbList query(String sql, Object... params) {
		return call(DbConfig.useSysDb(), sql, r -> read(r, null), false, params);
	}
	
	/**
	 * 执行查询
	 * @param ds 指定数据源
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static DbList query(DataSource ds, String sql, Object... params) {
		return call(ds, sql, r -> read(r, null), false, params);
	}
	
	/**
	 * 默认数据源：查询一条数据（只取结果集的第一条）
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static <T> Entity findOne(String sql, List<T> params) {
		return call(DbConfig.useSysDb(), sql, r -> readOne(r, null), false, params.toArray());
	}
	
	/**
	 * 查询一条数据（只取结果集的第一条）
	 * @param ds 指定数据源
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static <T> Entity findOne(DataSource ds, String sql, List<T> params) {
		return call(ds, sql, r -> readOne(r, null), false, params.toArray());
	}

	/**
	 * 默认数据源：查询一条数据（只取结果集的第一条）
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static Entity findOne(String sql, Object... params) {
		return call(DbConfig.useSysDb(), sql, r -> readOne(r, null), false, params);
	}
	
	/**
	 * 查询一条数据（只取结果集的第一条）
	 * @param ds 指定数据源
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static Entity findOne(DataSource ds, String sql, Object... params) {
		return call(ds, sql, r -> readOne(r, null), false, params);
	}

	/**
	 * 默认数据源：执行查询
	 * @param table 表名
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static <T> DbList queryByTable(String table, String sql, List<T> params) {
		return call(DbConfig.useSysDb(), sql, r -> read(r, table), false, params.toArray());
	}
	
	/**
	 * 执行查询
	 * @param ds 指定数据源
	 * @param table 表名
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static <T> DbList queryByTable(DataSource ds, String table, String sql, List<T> params) {
		return call(ds, sql, r -> read(r, table), false, params.toArray());
	}

	/**
	 * 默认数据源：执行查询
	 * @param table 表名
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static DbList queryByTable(String table, String sql, Object... params) {
		return call(DbConfig.useSysDb(), sql, r -> read(r, table), false, params);
	}
	
	/**
	 * 执行查询
	 * @param ds 指定数据源
	 * @param table 表名
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static DbList queryByTable(DataSource ds, String table, String sql, Object... params) {
		return call(ds, sql, r -> read(r, table), false, params);
	}
	
	/**
	 * 默认数据源：查询一条数据（只取结果集的第一条）
	 * @param table 表名
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static <T> Entity findOneByTable(String table, String sql, List<T> params) {
		return call(DbConfig.useSysDb(), sql, r -> readOne(r, table), false, params.toArray());
	}
	
	/**
	 * 查询一条数据（只取结果集的第一条）
	 * @param ds 指定数据源
	 * @param table 表名
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static <T> Entity findOneByTable(DataSource ds, String table, String sql, List<T> params) {
		return call(ds, sql, r -> readOne(r, table), false, params.toArray());
	}

	/**
	 * 默认数据源：查询一条数据（只取结果集的第一条）
	 * @param table 表名
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static Entity findOneByTable(String table, String sql, Object... params) {
		return call(DbConfig.useSysDb(), sql, r -> readOne(r, table), false, params);
	}
	
	/**
	 * 查询一条数据（只取结果集的第一条）
	 * @param ds 指定数据源
	 * @param table 表名
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 */
	public static Entity findOneByTable(DataSource ds, String table, String sql, Object... params) {
		return call(ds, sql, r -> readOne(r, table), false, params);
	}

	/**
	 * 默认数据源：查询count
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	public static <T> long count(String sql, List<T> params) {
		return count(DbConfig.useSysDb(), sql, params.toArray());
	}
	
	/**
	 * 查询count
	 * @param ds 指定数据源
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	public static <T> long count(DataSource ds, String sql, List<T> params) {
		return count(ds, sql, params.toArray());
	}

	/**
	 * 默认数据源：查询count
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	public static long count(String sql, Object... params) {
		return count(DbConfig.useSysDb(), sql, params);
	}
	
	/**
	 * 查询count
	 * @param ds 指定数据源
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	public static long count(DataSource ds, String sql, Object... params) {
		Long obj = val(ds, sql, params);
		return obj != null ? obj : 0;
	}

	/**
	 * 默认数据源：查询单个字段的值
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	public static <E, T> E val(String sql, List<T> params) {
		return val(DbConfig.useSysDb(), sql, params.toArray());
	}
	
	/**
	 * 查询单个字段的值
	 * @param ds 指定数据源
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	public static <E, T> E val(DataSource ds, String sql, List<T> params) {
		return val(ds, sql, params.toArray());
	}

	/**
	 * 默认数据源：查询单个字段的值
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	public static <E> E val(String sql, Object... params) {
		return val(DbConfig.useSysDb(), sql, params);
	}
	
	/**
	 * 查询单个字段的值
	 * @param ds 指定数据源
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	@SuppressWarnings({"unchecked"})
	public static <E> E val(DataSource ds, String sql, Object... params) {
		return call(ds, sql, r -> {
			if (r.next()) {
				Object val = r.getObject(1);
				if ((val instanceof BigDecimal)) {
					return (E) ((BigDecimal) val).toPlainString();
				} else {
					return (E) val;
				}
			}
			return null;
		}, false, params);
	}

	/**
	 * 默认数据源：查询单个字段的值
	 * @param sql 语句
	 * @param clazz 返回值的类型
	 * @param params 参数
	 * @return
	 */
	public static <E, T> E val(String sql, Class<E> clazz, List<T> params) {
		return val(DbConfig.useSysDb(), sql, clazz, params.toArray());
	}
	
	/**
	 * 查询单个字段的值
	 * @param ds 指定数据源
	 * @param sql 语句
	 * @param clazz 返回值的类型
	 * @param params 参数
	 * @return
	 */
	public static <E, T> E val(DataSource ds, String sql, Class<E> clazz, List<T> params) {
		return val(ds, sql, clazz, params.toArray());
	}

	/**
	 * 默认数据源：查询单个字段的值
	 * @param sql 语句
	 * @param clazz 返回值的类型
	 * @param params 参数
	 * @return
	 */
	public static <E> E val(String sql, Class<E> clazz, Object... params) {
		return val(DbConfig.useSysDb(), sql, clazz, params);
	}
	
	/**
	 * 查询单个字段的值
	 * @param ds 指定数据源
	 * @param sql 语句
	 * @param clazz 返回值的类型
	 * @param params 参数
	 * @return
	 */
	@SuppressWarnings({"unchecked"})
	public static <E> E val(DataSource ds, String sql, Class<E> clazz, Object... params) {
	    return call(ds, sql, r -> {
	        if (r.next()) {
	            Object val = r.getObject(1);
	            if (val == null) {
	                return null;
	            }
	            if (clazz == Integer.class) {
	                if (val instanceof BigDecimal) {
	                    return clazz.cast(((BigDecimal) val).intValue());
	                } else if (val instanceof Long) {
	                    return clazz.cast(((Long) val).intValue());
	                } else if (val instanceof String) {
	                    return clazz.cast(Integer.parseInt((String) val));
	                }
	            } else if (clazz == Long.class) {
	                if (val instanceof BigDecimal) {
	                    return clazz.cast(((BigDecimal) val).longValue());
	                } else if (val instanceof Integer) {
	                    return clazz.cast(((Integer) val).longValue());
	                } else if (val instanceof String) {
	                    return clazz.cast(Long.parseLong((String) val));
	                }
	            } else if (clazz == String.class) {
	                if (val instanceof BigDecimal) {
	                    return clazz.cast(((BigDecimal) val).toPlainString());
	                } else {
	                    return clazz.cast(val.toString());
	                }
	            }
	            // 其他类型的处理
	            return (E) val;
	        }
	        return null;
	    }, false, params);
	}

	/**
	 * 默认数据源：判断一个sql是否有数据
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	public static <T> boolean hasRow(String sql, List<T> params) {
		return call(DbConfig.useSysDb(), sql, r -> r.next(), false, params.toArray());
	}
	
	/**
	 * 判断一个sql是否有数据
	 * @param ds 指定数据源
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	public static <T> boolean hasRow(DataSource ds, String sql, List<T> params) {
		return call(ds, sql, r -> r.next(), false, params.toArray());
	}

	/**
	 * 默认数据源：判断一个sql是否有数据
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	public static boolean hasRow(String sql, Object... params) {
		return call(DbConfig.useSysDb(), sql, r -> r.next(), false, params);
	}
	
	/**
	 * 判断一个sql是否有数据
	 * @param ds 指定数据源
	 * @param sql 语句
	 * @param params 参数
	 * @return
	 */
	public static boolean hasRow(DataSource ds, String sql, Object... params) {
		return call(ds, sql, r -> r.next(), false, params);
	}
    
	/**
	 * 设置sql参数
	 * @param p PreparedStatement对象
	 * @param paramlist 参数列表
	 * @throws SQLException 
	 */
	private static void setParam(PreparedStatement p, Object... param) throws SQLException {
		for (int i = 0; i < param.length; i++) {
			p.setObject(i + 1, param[i]);
		}
	}
	
	/**
	 * 打印sql
	 * @param sql
	 * @param param
	 */
	private static void printSql(String text, String sql, Object... param) {
		if (log.isDebugEnabled()) {
			String formatSql = String.format("%s：%s", text, sql.replace("?", "'{}'"));
			log.debug(formatSql, param);
		}
	}
	
	/**
	 * 执行sql的公共方法
	 * @param ds 指定数据源
	 * @param sql sql语句
	 * @param f 在这个语句块中处理preparedStatement并获取自己想要的输出数据
	 * @param generateAutoKey 是否返回数据库的自增长主键
	 * @param param 参数列表
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static <T> T call(DataSource ds, String sql, FunctionWapper<ResultSet, T, SQLException> f, 
			boolean generateAutoKey, Object... param) {
		printSql("[db]执行sql", sql, param);
		// 将所有数据库资源对象放在下面这个对象里，方便关闭
		DbResource dbr = DbResource.create(ds);
		try {
			// 获取数据库连接
			Connection connection = dbr.getConnection();
			
			// 如果没有传f代码块，则认为是执行sql，需要开启事务
			if (f == null) connection.setAutoCommit(false);
			
			// 生成preparedStatement
			PreparedStatement preparedStatement = dbr.prepareStatement(sql, generateAutoKey);

			// 注入参数
			setParam(preparedStatement, param);
			
			// 如果没有传f代码块，则执行executeUpdate方法，返回影响行数；否则执行具体的f代码块
			if (f == null) {
				int i = preparedStatement.executeUpdate();
				
				T t = null;
				if (generateAutoKey) {
					// 返回自增长主键
					ResultSet rs = preparedStatement.getGeneratedKeys();
					if (rs.next()) t = (T)rs.getObject(1);
				} else {
					// 返回影响行数
					t = (T)Integer.valueOf(i);
				}
				
				// 提交事务
				connection.commit();
				
				return t;
			} else {
				ResultSet resultSet = dbr.executeQuery();
				return f.apply(resultSet);
			}
		} catch (SQLException e) {
			// 未开启自动事务时，才回滚
			if (f == null) {
				printSql("[db]执行sql失败，回滚事务", sql, param);
				dbr.rollback();
			} else {
				printSql("[db]执行sql失败", sql, param);
			}
			throw new HtSqlException("[db]sql执行异常：" + sql, e);
		} finally {
			dbr.close();
		}
	}
	
	/**
	 * 读取记录集
	 * @param resultSet 记录集
	 * @param table 表名
	 * @return
	 */
	private static DbList read(ResultSet resultSet, String table) {
		DbList dblist = DbList.create(table);
		try {
			ResultSetMetaData rsMetaData = resultSet.getMetaData();
			while (resultSet.next()) {
				dblist.add(createEntity(resultSet, table, rsMetaData));
			}
		} catch (Exception e) {
			log.error("[db]读取记录集发生异常：" + e.getMessage(), e);
			throw new RuntimeException("[db]读取记录集发生异常：" + e.getMessage(), e);
		}
		
		return dblist;
	}
	
	/**
	 * 读取记录集
	 * @param resultSet 记录集
	 * @param table 表名
	 * @return
	 */
	private static Entity readOne(ResultSet resultSet, String table) {
		Entity entity = null;
		try {
			if (resultSet.next()) {
				ResultSetMetaData rsMetaData = resultSet.getMetaData();
				entity = createEntity(resultSet, table, rsMetaData);
			} else {
				return null;
			}
		} catch (Exception e) {
			log.error("[db]读取记录集发生异常：" + e.getMessage(), e);
			throw new RuntimeException("[db]读取记录集发生异常：" + e.getMessage(), e);
		}
		
		return entity;
	}

	/**
	 * 创建一个实体
	 * @param resultSet 记录集
	 * @param table 表名
	 * @param rsMetaData 表元数据
	 * @return
	 * @throws SQLException
	 */
	private static Entity createEntity(ResultSet resultSet, String table, ResultSetMetaData rsMetaData) 
			throws SQLException {
		Entity entity = Entity.create(table);
		entity.getConfig().setIgnoreNullValue(false);

		// 遍历每一列，将列名和值放入 JSONObject
		int columnCount = rsMetaData.getColumnCount();
		for (int i = 1; i <= columnCount; i++) {
			String columnName = rsMetaData.getColumnName(i);
			Object columnValue = resultSet.getObject(i);

			if (columnValue != null && columnValue.getClass().getSimpleName().equals("Long")) {
				entity.setFromDb(columnName, String.valueOf(columnValue));
			} else {
				entity.setFromDb(columnName, columnValue);
			}
			
		}
		return entity;
	}

	/**
	 * 生成insert的sql语句
	 * @param bean 数据
	 * @return
	 */
	private static DbSql getInsertSql(Entity bean) {
		if (StrUtil.isBlank(bean.getTable())) {
			throw new RuntimeException("[db]未指定表名，无法执行sql~");
		}
		
		// 获取表的字段列表
		List<Column> columnList = DbConfig.columnList(bean.getTable());
		
		// 生成insert into语句开头部分
		StringBuilder sql = new StringBuilder(getFieldSql(bean.getTable(), columnList));

		// 生成values
		DbSql valueSql = getValueSqlBean(bean, columnList);
		sql.append(valueSql.getSql());
		
		return new DbSql(sql.toString(), valueSql.getParamsList());
	}

	/**
	 * 生成sql插入语句的insert into部分，如：INSERT INTO `user` (`id`, `name`, `sex`, `age`) VALUES 
	 * @param table 表名
	 * @param columnList 字段列表
	 * @return
	 */
	private static String getFieldSql(String table, List<Column> columnList) {
		StringBuilder sql = new StringBuilder();
		sql.append(String.format("insert into `%s` (", table));
		
		// 生成字段列表
		columnList.stream().forEach(c -> {
			sql.append(String.format("`%s`,", c.getCode()));
		});
		sql.deleteCharAt(sql.length() - 1).append(") values ");
		
		return sql.toString();
	}
	
	/**
	 * 生成sql插入语句的values后面的问号部分，如：(?,?,?,?)
	 * @param bean 数据
	 * @param columnList 字段列表
	 * @return 
	 */
	private static DbSql getValueSqlBean(Entity bean, List<Column> columnList) {
		StringBuilder sql = new StringBuilder("(");
		
		List<Object> paramlist = new ArrayList<>(); // 参数值列表
		for (int k = 0; k < columnList.size(); k++) {
			Column column = columnList.get(k);
			String field = column.getCode();

			// 保存值到参数列表
			if ("main_id".equals(field)) {
				paramlist.add(bean.getMainId());
			} else {
				Object val = bean.get(field);
				if (val == null || val == JSONNull.NULL || "null".equals(bean.getStr(field))) {
					paramlist.add(null);
				} else {
					paramlist.add(val.toString());
				}
			}
			
			// 生成sql的问号
			if ("bitmap".equals(column.getType())) {
				sql.append("to_bitmap(?),");
			} else {
				sql.append("?,");
			}
		}
		sql.deleteCharAt(sql.length() - 1).append(")");
		
		return new DbSql(sql.toString(), paramlist);
	}
	
	/**
	 * 排除一些有函数但又不需要修改的字段
	 * @param bean 待插入数据库的Entity
	 * @param columnList 字段列表
	 */
	private static void filterFunctionField(Entity bean, List<Column> columnList) {
		// 遍历表的字段列表，将有函数的字段值设为null（但要排除一些特定的）
		for (Column column : columnList) {
			String fun = column.getFun();
			String code = column.getCode();
			if (StrUtil.isNotBlank(fun) && !"REPLACE_IF_NOT_NULL".equals(fun) && !"KEY".equals(fun)
					&& !bean.getFunFields().contains(code)) {
				bean.set(code, null);
			}
		}
	}
}
